﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Runtime.Serialization;
using System.Windows;
using System.Windows.Controls;
using System.Windows.Media;
using System.Windows.Media.Imaging;
using Foundation;
using Vent.ViewModels.Tools;

namespace Vent.ViewModels
{
    public class TouchViewModel : ViewModel
    {
        private List<UIElement> _toches;
        private List<UIElement> _cutedToches;

        public TouchViewModel()
        {
            Initialize();
        }

        public Tool Tool
        {
            get { return Get(() => Tool); }
            set { Set(() => Tool, value); }
        }

        public int TouchsCount
        {
            get { return Get(() => TouchsCount); }
            set { Set(() => TouchsCount, value); }
        }

        public int TouchIndex
        {
            get { return Get(() => TouchIndex); }
            set { Set(() => TouchIndex, ManageTouchIndex(TouchIndex, value)); }
        }

        public Canvas Canvas { get; private set; }

        public List<Tool> Tools { get; private set; }

        public Brush Background
        {
            get { return Get(() => Background); }
            set { Set(() => Background, value); }
        }

        [OnDeserialized]
        public new void Initialize(StreamingContext context = default(StreamingContext))
        {
            _toches = new List<UIElement>();
            _cutedToches = new List<UIElement>();
            Background = new SolidColorBrush(Colors.White);
            Canvas = new Canvas
            {
                Background = Background,
                VerticalAlignment = VerticalAlignment.Stretch,
                HorizontalAlignment = HorizontalAlignment.Stretch,
            };

            Canvas.SizeChanged += (sender, args) =>
            {
                Canvas.Clip = new RectangleGeometry {Rect = new Rect(0, 0, Canvas.ActualWidth, Canvas.ActualHeight)};
            };

            PropertyChanged += (sender, args) => SharedCommands.UpdateCanExecuteState();
            this[SharedCommands.Back].CanExecute += (sender, args) => args.CanExecute = TouchIndex > 0;
            this[SharedCommands.Next].CanExecute += (sender, args) => args.CanExecute = TouchIndex < _toches.Count;
            this[SharedCommands.Back].Executed += (sender, args) => TouchIndex--;
            this[SharedCommands.Next].Executed += (sender, args) => TouchIndex++;
            this[SharedCommands.Save].Executed += (sender, args) =>
            {
                Background = Rasterize();
                _toches.RemoveRange(0, TouchIndex);
                TouchIndex = 0;
                TouchsCount = _toches.Count;
            };

            this[SharedCommands.Delete].CanExecute += (sender, args) => args.CanExecute = TouchIndex > 0;
            this[SharedCommands.Delete].Executed += (sender, args) =>
            {
                TouchIndex--;
                _toches.RemoveAt(TouchIndex);
                TouchsCount--;
            };

            this[SharedCommands.Cut].CanExecute += (sender, args) => args.CanExecute = TouchIndex > 0;
            this[SharedCommands.Cut].Executed += (sender, args) =>
            {
                TouchIndex--;
                _cutedToches.Add(_toches[TouchIndex]);
                _toches.RemoveAt(TouchIndex);
                TouchsCount--;
            };

            this[SharedCommands.Paste].CanExecute += (sender, args) => args.CanExecute = _cutedToches.Count > 0;
            this[SharedCommands.Paste].Executed += (sender, args) =>
            {
                var pastedTouchesCount = _cutedToches.Count;
                _toches.InsertRange(TouchIndex, _cutedToches);
                _cutedToches.Clear();
                TouchsCount = _toches.Count;
                TouchIndex += pastedTouchesCount;
            };

            this[SharedCommands.Dry].CanExecute += (sender, args) => args.CanExecute = _toches.Count > 0;
            this[SharedCommands.Dry].Executed += (sender, args) =>
            {
                Background = Rasterize();
                Clear();
            };

            this[() => Background].PropertyChanged += (sender, args) =>
            {
                Canvas.Children.Clear();
                Canvas.Background = Background;
                _toches.GetRange(0, TouchIndex).ForEach(Canvas.Children.Add);
            };

            Tools = new List<Tool>();
            Tool = Tools.FirstOrDefault();
            InitializeSingletouch();
        }

        private void InitializeSingletouch()
        {
            Canvas.MouseLeftButtonDown += (sender, args) => AddPoint(args.GetPosition(Canvas), true);
            Canvas.MouseLeftButtonUp += (sender, args) => AddPoint(args.GetPosition(Canvas), false);
            Canvas.MouseMove += (sender, args) => AddPoint(args.GetPosition(Canvas), false);
        }

        private void AddPoint(Point point, bool start)
        {
            if (Tool == null || point.X < 0 || point.Y < 0 || point.X >= Canvas.ActualWidth ||
                point.Y >= Canvas.ActualHeight)
                return;

            if (_toches.Any() && _toches.Count > TouchIndex)
                _toches.RemoveRange(TouchIndex, _toches.Count - TouchIndex);

            if (start) Rasterize();
            Tool.AddPoint(point, start);
        }

        private int ManageTouchIndex(int oldValue, int newValue)
        {
            if (newValue < 0 || newValue > _toches.Count)
                throw new Exception("Invalid touch index");

            if (oldValue <= newValue)
            {
                _toches.GetRange(oldValue, newValue - oldValue).ForEach(Canvas.Children.Add);
            }
            else if (Canvas.Children.Count >= oldValue - newValue)
            {
                _toches.GetRange(newValue, oldValue - newValue).ForEach(i => Canvas.Children.Remove(i));
            }
            else
            {
                Canvas.Children.Clear();
                Canvas.Background = Background;
                _toches.GetRange(0, newValue).ForEach(Canvas.Children.Add);
            }

            return newValue;
        }

        public void Clear()
        {
            _toches.Clear();
            Canvas.Children.Clear();
            Canvas.Background = Background;
            TouchsCount = TouchIndex = 0;
        }

        public void AddTouch(UIElement touch)
        {
            Rasterize();
            Canvas.Children.Add(touch);
            _toches.Add(touch);
            TouchsCount = _toches.Count;
            Set(() => TouchIndex, _toches.Count);
        }

        private Brush Rasterize()
        {
            if (!Canvas.Children.Any()) return Canvas.Background;
            var raster = new WriteableBitmap(Canvas, null);
            Canvas.Background = new ImageBrush
            {
                AlignmentX = AlignmentX.Left,
                AlignmentY = AlignmentY.Top,
                Stretch = Stretch.None,
                ImageSource = raster,
            };

            Canvas.Children.Clear();
            return new ImageBrush {ImageSource = raster};
        }
    }
}